#pragma once

#include <string>
#include <list>
#include <vector>

#include "visitor/VisitorFeature.h"
#include "parser/SQLParserFeature.h"
#include "ast/SQLObject.h"
#include "ast/statement/SQLSelectQueryBlock.h"

#include "../DbType.h"
#include "../utils/common_ptr.h"

class SchemaStatVisitor;
class SQLASTOutputVisitor;
class SQLExpr;
class SQLLimit;
class SQLPropertyExpr;
class SQLSelectOrderByItem;
class SQLStatement;
class SQLSelectItem;
class SQLBinaryOpExpr;
class SQLBinaryOperator;
class SQLName;
class SQLTableSource;
class SQLSelectQuery;
class SQLStatementImpl;
// class SQLSelectQueryBlock;

// static class TimeZoneVisitor : virtual public SQLASTVisitorAdapter {
//     TimeZone from;
//     TimeZone to;

//     TimeZoneVisitor(TimeZone from, TimeZone to) {
//         this->from = from;
//         this->to = to;
//     }

//     BOOL_ptr visit(SQLTimestampExpr x) {
//         SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

//         string_ptr  newTime = format->format(x->getDate(from));

//         x->setLiteral(newTime);

//         return BOOL::TRUE;
//     }

// };

class FormatOption
{
public:
  VisitorFeature_list_ptr lis_VisitorFeature;
  int features;

  FormatOption();

  FormatOption(VisitorFeature_list_ptr features);

  FormatOption(BOOL_ptr ucase);

  FormatOption(BOOL_ptr ucase, BOOL_ptr prettyFormat);

  FormatOption(BOOL_ptr ucase, BOOL_ptr prettyFormat, BOOL_ptr parameterized);

  BOOL_ptr isDesensitize();

  void setDesensitize(BOOL_ptr val);

  BOOL_ptr isUppCase();

  void setUppCase(BOOL_ptr val);

  BOOL_ptr isPrettyFormat();

  void setPrettyFormat(BOOL_ptr prettyFormat);

  BOOL_ptr isParameterized();

  void setParameterized(BOOL_ptr parameterized);

  void config(VisitorFeature_ptr feature, BOOL_ptr state);

  BOOL_ptr isEnabled(VisitorFeature_ptr feature);
};

class SQLUtils
{
public:
  static BOOL_ptr __init;
  static BOOL_ptr init();
  // static Charset UTF8 = Charset->forName("UTF-8");

  static SQLParserFeature_list_ptr FORMAT_DEFAULT_FEATURES;

  static FormatOption_ptr DEFAULT_FORMAT_OPTION;
  static FormatOption_ptr DEFAULT_LCASE_FORMAT_OPTION;

  // static Log LOG ;

  static string_ptr toSQLString(Object_ptr sqlObject, DbType_ptr dbType);
  static string_ptr toSQLString(SQLObject_ptr obj);
  static string_ptr toSQLString(SQLObject_ptr sqlObject, DbType_ptr dbType);
  static string_ptr toSQLString(SQLObject_ptr sqlObject, DbType_ptr dbType, FormatOption_ptr option);
  static string_ptr toSQLString(
    SQLObject_ptr sqlObject, 
    DbType_ptr dbType, 
    FormatOption_ptr option, 
    VisitorFeature_list_ptr features);
  static string_ptr toSQLString(
    SQLStatementImpl_ptr sqlObject, 
    DbType_ptr dbType, 
    FormatOption_ptr option, 
    VisitorFeature_list_ptr features);
  static string_ptr toSQLString(SQLStatement_list_ptr statementList, DbType_ptr dbType);
  static string_ptr toSQLString(SQLStatement_list_ptr statementList, DbType_ptr dbType, FormatOption_ptr option);
  static string_ptr toSQLString(SQLStatement_list_ptr statementList, DbType_ptr dbType, Object_list_ptr parameters);
  static string_ptr toSQLString(
    SQLStatement_list_ptr statementList, 
    DbType_ptr dbType, 
    Object_list_ptr parameters, 
    FormatOption_ptr option);
  static string_ptr toSQLString(
    SQLStatement_list_ptr statementList, 
    DbType_ptr dbType, 
    Object_list_ptr parameters, 
    FormatOption_ptr option, 
    std::shared_ptr<std::map<std::string, string_ptr>> tableMapping);

  static string_ptr toOdpsString(SQLObject_ptr sqlObject);

  static string_ptr toHiveString(SQLObject_ptr sqlObject);

  static string_ptr toOdpsString(SQLObject_ptr sqlObject, FormatOption_ptr option);

  static string_ptr toAntsparkString(SQLObject_ptr sqlObject);

  static string_ptr toAntsparkString(SQLObject_ptr sqlObject, FormatOption_ptr option);

  static string_ptr toMySqlString(SQLObject_ptr sqlObject);

  static string_ptr toMySqlStringIfNotNull(SQLObject_ptr sqlObject, string_ptr defaultStr);

  static string_ptr toMySqlString(SQLObject_ptr sqlObject, VisitorFeature_list_ptr features);

  static string_ptr toNormalizeMysqlString(SQLObject_ptr sqlObject);

  static string_ptr toMySqlString(SQLObject_ptr sqlObject, FormatOption_ptr option);

  static SQLExpr_ptr toMySqlExpr(string_ptr sql);

  static string_ptr formatMySql(string_ptr sql);

  static string_ptr formatMySql(string_ptr sql, FormatOption_ptr option);

  static string_ptr formatOracle(string_ptr sql);

  static string_ptr formatOracle(string_ptr sql, FormatOption_ptr option);

  static string_ptr formatOdps(string_ptr sql);

  static string_ptr formatPresto(string_ptr sql);

  static string_ptr formatPresto(string_ptr sql, FormatOption_ptr option);

  static string_ptr formatHive(string_ptr sql);

  static string_ptr formatOdps(string_ptr sql, FormatOption_ptr option);

  static string_ptr formatHive(string_ptr sql, FormatOption_ptr option);

  static string_ptr formatSQLServer(string_ptr sql);

  static string_ptr toOracleString(SQLObject_ptr sqlObject);

  static string_ptr toOracleString(SQLObject_ptr sqlObject, FormatOption_ptr option);

  static string_ptr toPGString(SQLObject_ptr sqlObject);

  static string_ptr toPGString(SQLObject_ptr sqlObject, FormatOption_ptr option);

  static string_ptr toDB2String(SQLObject_ptr sqlObject);

  static string_ptr toDB2String(SQLObject_ptr sqlObject, FormatOption_ptr option);

  static string_ptr toSQLServerString(SQLObject_ptr sqlObject);

  static string_ptr toSQLServerString(SQLObject_ptr sqlObject, FormatOption_ptr option);

  static string_ptr formatPGSql(string_ptr sql, FormatOption_ptr option);

  static SQLExpr_ptr toSQLExpr(string_ptr sql, DbType_ptr dbType);

  static SQLSelectOrderByItem_ptr toOrderByItem(string_ptr sql, DbType_ptr dbType);

  // static SQLUpdateSetItem* toUpdateSetItem(string_ptr  sql, DbType_ptr   dbType);

  static SQLSelectItem_ptr toSelectItem(string_ptr sql, DbType_ptr dbType);

  static SQLStatement_list_ptr toStatementList(string_ptr sql, DbType_ptr dbType);

  static SQLExpr_ptr toSQLExpr(string_ptr sql);

  // static string_ptr  format(string_ptr  sql, string_ptr  dbType);

  static string_ptr format(string_ptr sql, DbType_ptr dbType);

  static string_ptr format(string_ptr sql, DbType_ptr dbType, FormatOption_ptr option);

  static string_ptr format(string_ptr sql, DbType_ptr dbType, Object_list_ptr parameters);

  static string_ptr format(string_ptr sql, DbType_ptr dbType, Object_list_ptr parameters, FormatOption_ptr option);

  static string_ptr format(
      string_ptr sql,
      DbType_ptr dbType,
      Object_list_ptr parameters,
      FormatOption_ptr option,
      SQLParserFeature_list_ptr features);

  static SQLASTOutputVisitor_ptr createOutputVisitor(std::stringstream *out, DbType_ptr dbType);

  static SQLASTOutputVisitor_ptr createFormatOutputVisitor(
      std::stringstream *out,
      SQLStatement_list_ptr statementList,
      DbType_ptr dbType);

  static SchemaStatVisitor_ptr createSchemaStatVisitor(SQLStatement_list_ptr statementList, DbType_ptr dbType);
  static SchemaStatVisitor_ptr createSchemaStatVisitor(DbType_ptr dbType);
  static SchemaStatVisitor_ptr createSchemaStatVisitor(SchemaRepository_ptr repository);
  static SchemaStatVisitor_ptr createSchemaStatVisitor(SchemaRepository_ptr repository, DbType_ptr dbType);

  // static std::list<SQLStatement_ptr > parseStatements(string_ptr  sql, string_ptr  dbType, std::list<SQLParserFeature *> features);

  static SQLStatement_list_ptr parseStatements(
      string_ptr sql,
      DbType_ptr dbType,
      SQLParserFeature_list_ptr features);

  static SQLStatement_list_ptr parseStatements(
      string_ptr sql,
      DbType_ptr dbType,
      BOOL_ptr keepComments);

  // static std::list<SQLStatement_ptr > parseStatements(string_ptr  sql, string_ptr  dbType);

  static SQLStatement_list_ptr parseStatements(string_ptr sql, DbType_ptr dbType);

  static SQLStatement_ptr parseSingleStatement(string_ptr sql, DbType_ptr dbType, BOOL_ptr keepComments);

  // static SQLStatement_ptr parseSingleStatement(string_ptr  sql, string_ptr  dbType, std::shared_ptr<std::list<SQLParserFeature *>> features);

  static SQLStatement_ptr parseSingleStatement(
      string_ptr sql,
      DbType_ptr dbType,
      SQLParserFeature_list_ptr features);

  static SQLStatement_ptr parseSingleMysqlStatement(string_ptr sql);

  /**
   * @param columnName
   * @param tableAlias
   * @param pattern    if pattern is NULL,it will be set {%Y-%m-%d %H:%i:%s} as mysql default value and set {yyyy-mm-dd
   *                   hh24:mi:ss} as oracle default value
   * @param dbType     {@link DbType_ptr   } if dbType is NULL ,it will be set the mysql as a default value
   * @author owenludong->lud
   */
  static string_ptr buildToDate(string_ptr columnName, string_ptr tableAlias, string_ptr pattern, DbType_ptr dbType);

  static SQLExpr_list_ptr split(SQLBinaryOpExpr_ptr x);

  static string_ptr translateOracleToMySql(string_ptr sql);

  static string_ptr addCondition(string_ptr sql, string_ptr condition, DbType_ptr dbType);
  static string_ptr addCondition(string_ptr sql, string_ptr condition, SQLBinaryOperator_ptr op, BOOL_ptr left, DbType_ptr dbType);
  static void addCondition(SQLStatement_ptr stmt, SQLBinaryOperator_ptr op, SQLExpr_ptr condition, BOOL_ptr left);

  static SQLExpr_ptr buildCondition(SQLBinaryOperator_ptr op, SQLExpr_ptr condition, BOOL_ptr left, SQLExpr_ptr where);

  static string_ptr addSelectItem(string_ptr selectSql, string_ptr expr, string_ptr alias, DbType_ptr dbType);

  // static void acceptBooleanOr(string_ptr  sql, DbType_ptr    dbType, Consumer<SQLBinaryOpExprGroup> consumer) ;

  // static void acceptBinaryOpExprGroup(string_ptr  sql,
  //                                            DbType_ptr    dbType,
  //                                            Consumer<SQLBinaryOpExprGroup> consumer,
  //                                            Predicate<SQLBinaryOpExprGroup> filter) ;

  // static void acceptBinaryOpExpr(string_ptr  sql,
  //                                       DbType_ptr    dbType,
  //                                       Consumer<SQLBinaryOpExpr> consumer,
  //                                       Predicate<SQLBinaryOpExpr> filter) ;

  // static void acceptTableSource(string_ptr  sql,
  //                                      DbType_ptr    dbType,
  //                                      Consumer<SQLTableSource_ptr > consumer,
  //                                      Predicate<SQLTableSource_ptr > filter) ;

  // static void acceptSelectQueryBlock(string_ptr  sql,
  //                                           DbType_ptr    dbType,
  //                                           Consumer<SQLSelectQueryBlock*> consumer,
  //                                           Predicate<SQLSelectQueryBlock*> filter) ;

  // static void acceptAggregateFunction(string_ptr  sql,
  //                                            DbType_ptr    dbType,
  //                                            Consumer<SQLAggregateExpr> consumer,
  //                                            Predicate<SQLAggregateExpr> filter) ;

  // static void acceptFunction(string_ptr  sql,
  //                                   DbType_ptr    dbType,
  //                                   Consumer<SQLMethodInvokeExpr> consumer,
  //                                   Predicate<SQLMethodInvokeExpr> filter) ;

  // static void acceptInsertInto(string_ptr  sql,
  //                                     DbType_ptr    dbType,
  //                                     Consumer<SQLInsertInto> consumer,
  //                                     Predicate<SQLInsertInto> filter) ;

  static string_ptr addSelectItem(string_ptr selectSql, string_ptr expr, string_ptr alias, BOOL_ptr first, DbType_ptr dbType);
  static void addSelectItem(SQLStatement_ptr stmt, SQLExpr_ptr expr, string_ptr alias, BOOL_ptr first);
  static void addSelectItem(SQLSelectQueryBlock_ptr queryBlock, SQLExpr_ptr expr, string_ptr alias, BOOL_ptr first);

  static string_ptr refactor(string_ptr sql, DbType_ptr dbType, std::shared_ptr<std::map<std::string, string_ptr>> tableMapping);

  static long hash(string_ptr sql, DbType_ptr dbType);

  static SQLExpr_ptr not_(SQLExpr_ptr expr);

  // static string_ptr normalize(string_ptr name);
  static string_ptr normalize(string_ptr name);
  static string_ptr normalize(string_ptr name, BOOL_ptr isTrimmed);
  // static string_ptr normalize(string_ptr name, DbType_ptr dbType);
  static string_ptr normalize(string_ptr name, DbType_ptr dbType);

  static string_ptr _normalize(string_ptr name, DbType_ptr dbType, BOOL_ptr isForced);
  static string_ptr _normalize(string_ptr name, DbType_ptr dbType, BOOL_ptr isForced, BOOL_ptr isTrimmed);

  static string_ptr forcedNormalize(string_ptr name, DbType_ptr dbType);

  static bool nameEquals(SQLName_ptr a, SQLName_ptr b);
  static bool nameEquals(string_ptr a, string_ptr b);
  // static BOOL_ptr nameEquals(string_ptr a, string_ptr b);

  static BOOL_ptr isValue(SQLExpr_ptr expr);

  static BOOL_ptr replaceInParent(SQLExpr_ptr expr, SQLExpr_ptr target);
  static BOOL_ptr replaceInParent(SQLTableSource_ptr cmp, SQLTableSource_ptr dest);
  static BOOL_ptr replaceInParent(SQLSelectQuery_ptr cmp, SQLSelectQuery_ptr dest);

  static string_ptr desensitizeTable(string_ptr tableName);

  /**
   * 重新排序建表语句，解决建表语句的依赖关系
   *
   * @param sql
   * @param dbType
   */
  static string_ptr sort(string_ptr sql, DbType_ptr dbType);

  /**
   * @param query
   * @param dbType
   * @return 0：sql->toString, 1:
   */
  // static std::list<Object_ptr > clearLimit(string_ptr  query, DbType_ptr   dbType);

  static SQLLimit_ptr clearLimit(SQLSelectQueryBlock_ptr queryBlock);

  static SQLLimit_ptr getLimit(SQLStatement_ptr statement, DbType_ptr dbType);
  static SQLLimit_ptr getLimit(string_ptr query, DbType_ptr dbType);

  // static string_ptr  convertTimeZone(string_ptr  sql, TimeZone from, TimeZone to) ;

  // static SQLStatement_ptr  convertTimeZone(SQLStatement_ptr  stmt, TimeZone from, TimeZone to) ;

  // static std::list<SQLInsertStatement *> splitInsertValues(DbType_ptr   dbType, string_ptr  insertSql, int size);
};
